package xyz.syyrjx.call.proxy;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.syyrjx.beaker.annotation.SyyrjxRpcBreaker;
import xyz.syyrjx.exception.SyyrjxRpcBreakerException;
import xyz.syyrjx.consumer.client.SyyrjxRpcConsumerClient;
import xyz.syyrjx.exception.SyyrjxRpcBreakerException$RemoteCallException;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 大地崩坏苍蝇兽
 * @date 2023/1/22 - 15:29
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SyyrjxRpcCallClientProxy implements InvocationHandler {

    private final static Logger LOGGER = LoggerFactory.getLogger(SyyrjxRpcCallClientProxy.class);

    //声明式调用接口
    private Class<?> interfaceClass;

    //降级类对象
    private Object fallbackEntity;

    //超时时间
    private long timeout;

    //需要忽略的异常
    private Class<? extends Exception>[] ignoreExpects;

    //接口及其连续失败次数映射表
    private final static Map<Class<?>, Integer> API_FAIL_COUNT_MAP = new ConcurrentHashMap<>();

    @Setter
    private static SyyrjxRpcConsumerClient client;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!API_FAIL_COUNT_MAP.containsKey(interfaceClass)) {
            API_FAIL_COUNT_MAP.put(interfaceClass, 0);
        }
        int count = API_FAIL_COUNT_MAP.get(interfaceClass);

        Class<? extends Exception>[] methodIgnore = null;
        long methodTimeout = 0;
                SyyrjxRpcBreaker breaker = method.getAnnotation(SyyrjxRpcBreaker.class);
        if (breaker != null) {
            methodIgnore = breaker.ignoreException();
            methodTimeout = breaker.timeout();
        }

        long useTimeout = methodTimeout > this.timeout ? methodTimeout : this.timeout;

        //连续调用十次出现问题，熔断该服务，不再远程调用
        if (count < 10) {
            try {
                LOGGER.debug("代理调用发送服务");
                Object res = client.remoteCall(interfaceClass.getName(), method.getName(), useTimeout, args);
                count -= 1;
                API_FAIL_COUNT_MAP.put(interfaceClass, (count > 0 ? count : 0));
                return res;
            } catch (Exception e) {
                //确定抛出的异常
                Throwable caues = e;
                if (Objects.equals(e.getClass(), java.util.concurrent.ExecutionException.class)){
                    caues = caues.getCause();
                }
                //不需要回调或者异常被忽略
                if (fallbackEntity == null
                        || isIgnore(caues,this.ignoreExpects)
                        || isIgnore(caues,methodIgnore)) {
                    throw new SyyrjxRpcBreakerException$RemoteCallException();
                } else {
                    //确定是超时还是其它异常
                    API_FAIL_COUNT_MAP.put(interfaceClass, count + 1);
                    return fallback(caues, method, args);
                }
            }
        } else {
            return fallback(new SyyrjxRpcBreakerException(SyyrjxRpcBreakerException.SERVICE_HAS_BEEN_BREAK)
                    , method, args);
        }
    }

    public Object fallback(Throwable caues, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        String fallbackMethodName = method.getName();
        Object[] newArgs = args;
        if (Objects.equals(caues.getClass(), java.util.concurrent.TimeoutException.class)) {
            fallbackMethodName += "Timeout";
        }else {
            fallbackMethodName += "Exception";
            //调整入参
            int length = 1;
            if (args != null){
                length = args.length + 1;
            }
            newArgs = new Object[length];
            newArgs[0] = caues;
            for (int i = 1; i < length; i++) {
                newArgs[i] = args[i - 1];
            }
        }
        //拿到回调方法
        Method[] methods = fallbackEntity.getClass().getMethods();
        Method fallbackMethod = null;
        for (Method m : methods) {
            if (Objects.equals(m.getName(), fallbackMethodName)) {
                fallbackMethod = m;
                break;
            }
        }
        if (fallbackMethod == null) {
            throw new SyyrjxRpcBreakerException(SyyrjxRpcBreakerException.NOT_DEFINE_FALLBACK_METHOD);
        }
        return fallbackMethod.invoke(fallbackEntity, newArgs);
    }

    private boolean isIgnore(Throwable throwable, Class<? extends Exception>[] array) {
        LOGGER.debug("忽略异常判断：" + throwable);
        LOGGER.debug("忽略列表：" + Arrays.toString(array));

        //获取当前异常的全部父类
        List<Class<?>> throwableParents = new ArrayList<>();
        throwableParents.add(throwable.getClass());
        for (int i = 0; i < throwableParents.size(); i++){
            Class<?> currentClass = throwableParents.get(i);
            Class<?> superclass = currentClass.getSuperclass();
            if (!Objects.equals(superclass, Object.class)) {
                throwableParents.add(superclass);
            }
        }

        //判断是否需要忽略
        if (array != null) {
            for (Class<? extends Exception> ignoreExpect : array) {
                if (throwableParents.contains(ignoreExpect)) {
                    return true;
                }
            }
        }
        return false;
    }


}
